# -*- coding: utf-8 -*-

try:
    import _path
except:
    pass


if __name__ == '__main__':

    import sys
    import traceback
    import random
    import math

    import pygame

    import pyknicpygame
    pyknicpygame.init()
    from pyknicpygame.spritesystem import *
    from pyknic.mathematics import Vec2 as Vec
    from pyknic.mathematics import Point2D as Point


    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    SCREENSIZE = (800, 600)

    def random_rgb():
        return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

    def invert_color(col):
        return (255 - col[0], 255 - col[1], 255 - col[2], col[3] if len(col) == 4 else 255)

    def create_arrows_sprites():
        # font = pygame.font.Font(None, 12)
        sprites = [
            VectorSprite(Vec(20, 0), Point(0.0, 0.0), color=(255,0,0,128)),
            # TextSprite("x", Vec(20, 10), font=font, color=(255,0,0,128)),
            VectorSprite(Vec(0, 20), Point(0.0, 0.0), color=(0,255,0,128)),
            # TextSprite("y", Vec(-10, 20), font=font, color=(0,255,0,128)),
            ]
        return sprites

    def create_random_ppalpha_surf(col, alpha=128):
        surf = pygame.Surface((random.randint(5, 100), random.randint(5, 100)), pygame.SRCALPHA|pygame.RLEACCEL)
        surf.fill(col)
        # make transparent hohle 
        rect = surf.get_rect()
        rect.inflate_ip(-rect.w * 0.5, -rect.h * 0.5)
        surf.fill((255, 255, 255, 0), rect)
        return surf
        
    def create_random_surf(col):
        surf = pygame.Surface((random.randint(5, 100), random.randint(5, 100)), pygame.RLEACCEL)
        surf.fill(col)
        return surf

    class BallEnt(Sprite):

        def __init__(self, pos, velo, image=None):
            self.vel = velo
            if image is None:
                col = random.choice(list(pygame.color.THECOLORS.values()))
                Sprite.__init__(self, create_random_surf(col), pos, anchor="center")
            else:
                Sprite.__init__(self, image, pos, anchor="center")
                
        def update(self, dt):
            self.old_position.x = self.position.x
            self.old_position.y = self.position.y
            self.position.x = self.position.x  + self.vel.x * dt
            self.position.y = self.position.y  + self.vel.y * dt

            if self.position.x > SCREENSIZE[0]:
                self.position.x = SCREENSIZE[0]
                self.vel.x = -self.vel.x
            if self.position.x < 0:
                self.position.x = 0
                self.vel.x = -self.vel.x

            if self.position.y > SCREENSIZE[1]:
                self.position.y = SCREENSIZE[1]
                self.vel.y = -self.vel.y
            if self.position.y < 0:
                self.position.y = 0
                self.vel.y = -self.vel.y

    class BallEntExt(BallEnt):
    
        def __init__(self, pos, velo):
            BallEnt.__init__(self, pos, velo)
            self.rotation = random.randint(0, 360)
            self.zoom = random.random() * 2 + 0.1
            self.zoom_change = random.random() * 0.0051
            self.rot_speed = (random.random() * 2 - 1) * 0.05
            
        def update(self, dt):
        
            self.rotation = self.rotation + self.rot_speed * dt
            self.zoom = self.zoom + self.zoom_change * dt
            if self.zoom > 2.0:
                self.zoom_change *= -1
                self.zoom = 2.0
            if self.zoom <= 0:
                self.zoom_change *= -1
                self.zoom = 0.1
        
            BallEnt.update(self, dt)


    def demo_interpolation(screen, renderer):

        renderer = DefaultRenderer()
        renderer2 = DefaultRenderer()
        
        arrows = create_arrows_sprites()
        renderer.add_sprites(arrows)
        renderer2.add_sprites(arrows)
        
        balls = []
        balls2 = []
        num = 50
        for i in range(num):
            pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
            vel = Vec(random.randint(-5, 5), random.randint(-5, 5)) * 0.1
            balls.append(BallEnt(pos, vel))
            
            ent = BallEnt(Vec(pos.x, pos.y, 1), Vec(vel.x, vel.y))
            ent.set_image(balls[-1]._image.copy())
            balls2.append(ent)
        renderer.add_sprites(balls)
        renderer2.add_sprites(balls2)
            
        # static sprites
        for y in range(0, 500, 20):
            for x in range(0, 500, 20):
                # pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
                pos = Point(x, y)
                spr1 = Sprite(create_random_surf(random_rgb()), pos, z_layer=-1)
                renderer.add_sprite(spr1)
                spr = Sprite(spr1.image, pos, z_layer=-1)
                renderer2.add_sprite(spr)

        hud_renderer = HudRenderer()
        screen_rect = screen.get_rect()
        cx, cy = screen_rect.center

        tspr = TextSprite("use arrow keys to move", Point(cx, SCREENSIZE[1] - 50), color=WHITE, \
                                                z_layer=1000, anchor='center', parallax_factors=Vec(0.0, 0.0))
        hud_renderer.add_sprite(tspr)
        tspr = TextSprite("interpolated", Point(50, SCREENSIZE[1] - 50), color=WHITE, \
                                                z_layer=1000, anchor='midleft', parallax_factors=Vec(0.0, 0.0))
        hud_renderer.add_sprite(tspr)
        tspr = TextSprite("ordinary dt update", Point(SCREENSIZE[0] - 50, SCREENSIZE[1] - 50), color=WHITE, \
                                                z_layer=1000, anchor='midright', parallax_factors=Vec(0.0, 0.0))
        hud_renderer.add_sprite(tspr)

        cam_hud = Camera(pygame.Rect((0, 0), screen_rect.size) , position=Point())
        cam1 = Camera(pygame.Rect((0, 0), screen_rect.midbottom).inflate(-5, -5) , position=Point(cx, cy))
        cam2 = Camera(pygame.Rect(screen_rect.midtop, screen_rect.midbottom).inflate(-5, -5) , position=Point(cx, cy))
        # # # cams = [cam1, cam2]

        running = True
        clock = pygame.time.Clock()
        step_time = 10.0 # 100 fps
        accumulator = 0
        while running:
            dto = clock.tick(20)# * random.random()
            dt = dto
            if dt > 20:
                dt = 20
            
            # interpolated update
            accumulator += dt
            while accumulator >= step_time:
            
                for ball in balls:
                    ball.update(step_time)
                    
                accumulator -= step_time
                
            alpha = accumulator / step_time
            
            # not interpolated updated, just ordinary dt update
            for ball in balls2:
                ball.update(dt)

            # render
            screen.fill((255, 00, 00))
            renderer.draw(screen, cam1, fill_color=BLACK, interpolation_factor=alpha)
            renderer2.draw(screen, cam2, fill_color=BLACK)
            hud_renderer.draw(screen, cam_hud, fill_color=None)
            pygame.display.flip()

            # event handling
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        return
                    elif event.key == pygame.K_c:
                        # for cam in cams:
                        cam1.use_conversion_methods = not cam1.use_conversion_methods
                        cam2.use_conversion_methods = not cam2.use_conversion_methods
                        cam_hud.use_conversion_methods = not cam_hud.use_conversion_methods
                        print("cam.use_conversion_methods", cam1.use_conversion_methods)
                    elif event.key == pygame.K_F2:
                        print(clock.get_fps())
                elif event.type == pygame.QUIT:
                    return
            # move cams
            pressed = pygame.key.get_pressed()
            dir = Vec(0.0, 0.0)
            if pressed[pygame.K_RIGHT]:
                dir += Vec(1, 0)
            if pressed[pygame.K_LEFT]:
                dir -= Vec(1, 0)
            if pressed[pygame.K_UP]:
                dir += Vec(0, -1)
            if pressed[pygame.K_DOWN]:
                dir += Vec(0, 1)
            dir.length = 5
            cam1.position += dir
            cam2.position += dir
 

    def demo_viewport(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())

        num = 200
        for i in range(num):
            col = random.choice(list(pygame.color.THECOLORS.values()))
            surf = create_random_surf(col)
            pos = Point(random.randint(-390, 390), random.randint(-290, 290))
            spr = Sprite(surf, pos, z_layer=100, anchor='center')
            renderer.add_sprite(spr)

        cam = Camera(pygame.Rect(350, 250, 100, 100))
        cam.use_conversion_methods = False
        running = True
        clock = pygame.time.Clock()
        while running:
            clock.tick(50)
            screen.fill(BLACK)
            renderer.draw(screen, cam, do_flip=True)
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        return
                    elif event.key == pygame.K_c:
                        cam.use_conversion_methods = not cam.use_conversion_methods
                        print("cam.use_conversion_methods", cam.use_conversion_methods)
                    elif event.key == pygame.K_F2:
                        print(clock.get_fps())
                elif event.type == pygame.QUIT:
                    return

            pressed = pygame.key.get_pressed()
            direction = Vec(0.0, 0.0)
            if pressed[pygame.K_RIGHT]:
                direction += Vec(1, 0)
            if pressed[pygame.K_LEFT]:
                direction -= Vec(1, 0)
            if pressed[pygame.K_UP]:
                direction += Vec(0, -1)
            if pressed[pygame.K_DOWN]:
                direction += Vec(0, 1)
            direction.length = 2
            # round is needed because the vieport rect only can handle integer coordinates!!
            # otherwise strange roundin error effects occure
            direction.round(int)
            crect = cam.rect
            cam.position = cam.position + direction
            cam.rect = pygame.Rect((crect.left + direction.x, crect.top + direction.y), crect.size)
    
    def demo_sprite_performance(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())

        num = 10
        balls = []
        for i in range(num):
            pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
            vel = Vec(random.randint(-5, 5), random.randint(-5, 5)) * 0.05
            balls.append(BallEnt(pos, vel))
        renderer.add_sprites(balls)
        text = TextSprite("fps: ", Point(200, 250), color=(255, 255, 0), \
                                            z_layer=10000, anchor='center', parallax_factors=Vec(0.0, 0.0))
        renderer.add_sprite(text)

        cam = Camera(screen.get_rect(), position=Point(400, 300))

        running = True
        clock = pygame.time.Clock()
        time = 0
        while running:
            dt = clock.tick(50)
            time += dt
            if time > 1:
                time = 0
                fps = clock.get_fps()
                if fps < 20:
                    x = balls.pop()
                    renderer.remove_sprite(x)
                if fps > 21:
                    pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
                    vel = Vec(random.randint(-5, 5), random.randint(-5, 5)) * 0.05
                    ball = BallEnt(pos, vel)
                    balls.append(ball)
                    renderer.add_sprite(ball)
                text.text = "fps: " + str(round(clock.get_fps(), 2)) + "    count: " + str(len(balls))
            renderer.draw(screen, cam, fill_color=BLACK, do_flip=True)
            
            for ball in balls:
                ball.update(dt)

            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        return
                    elif event.key == pygame.K_c:
                        cam.use_conversion_methods = not cam.use_conversion_methods
                        print("cam.use_conversion_methods", cam.use_conversion_methods)
                    elif event.key == pygame.K_F2:
                        print(clock.get_fps())
                elif event.type == pygame.QUIT:
                    return

    
    def demo_transformation_performance(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())

        num = 10
        balls = []
        for i in range(num):
            pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
            vel = Vec(random.randint(-5, 5), random.randint(-5, 5)) * 0.051
            balls.append(BallEntExt(pos, vel))
        renderer.add_sprites(balls)
        text = TextSprite("fps: ", Point(200, 250), color=(255, 255, 0), \
                                                        z_layer=10000, anchor='center', parallax_factors=Vec(0.0, 0.0))
        renderer.add_sprite(text)

        cam = Camera(screen.get_rect(), position=Point(400, 300))

        running = True
        clock = pygame.time.Clock()
        time = 0
        while running:
            dt = clock.tick(50)
            time += dt
            if time > 1:
                time = 0
                fps = clock.get_fps()
                if fps < 20:
                    if len(balls) > 1:
                        x = balls.pop()
                        renderer.remove_sprite(x)
                if fps > 25:
                    pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
                    vel = Vec(random.randint(-5, 5), random.randint(-5, 5)) * 0.051
                    ball = BallEntExt(pos, vel)
                    ball.z_layer = len(balls) + 1
                    balls.append(ball)
                    renderer.add_sprite(ball)
                text.text = "fps: " + str(round(clock.get_fps())) + "    count: " + str(len(balls))
            renderer.draw(screen, cam, fill_color=BLACK, do_flip=True)

            for ball in balls:
                ball.update(dt)

            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        return
                    elif event.key == pygame.K_c:
                        cam.use_conversion_methods = not cam.use_conversion_methods
                        print("cam.use_conversion_methods", cam.use_conversion_methods)
                    elif event.key == pygame.K_F2:
                        print(clock.get_fps())
                elif event.type == pygame.QUIT:
                    return


    def demo_split_screen(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())

        num = 20
        balls = []
        for i in range(num):
            pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
            vel = Vec(random.randint(-5, 5), random.randint(-5, 5)) * 0.05
            balls.append(BallEnt(pos, vel))
        renderer.add_sprites(balls)
        for i in range(int(num)):
            pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
            spr = Sprite(create_random_surf((128, 128, 128)), pos)
            renderer.add_sprite(spr)

        hud_renderer = HudRenderer()

        tspr = TextSprite("use A,S,W,D to move", Point(200, 50), color=WHITE, \
                                                z_layer=1000, anchor='center', parallax_factors=Vec(0.0, 0.0))
        hud_renderer.add_sprite(tspr)
        tspr = TextSprite("use arrow keys to move", Point(600, 50), color=WHITE, \
                                                z_layer=1000, anchor='center', parallax_factors=Vec(0.0, 0.0))
        hud_renderer.add_sprite(tspr)

        screen_rect = screen.get_rect()
        cam1 = Camera(pygame.Rect((0, 0), screen_rect.midbottom).inflate(-5, -5) , position=Point())
        cam2 = Camera(pygame.Rect(screen_rect.midtop, screen_rect.midbottom).inflate(-5, -5) , position=Point())
        cams = [cam1, cam2]

        running = True
        clock = pygame.time.Clock()
        while running:
            dt = clock.tick(50)
            screen.fill((255, 00, 00))
            for cam in cams:
                renderer.draw(screen, cam, fill_color=BLACK, do_flip=False)
                hud_renderer.draw(screen, cam, fill_color=None, do_flip=False)
            pygame.display.flip()

            for ball in balls:
                ball.update(dt)

            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        return
                    elif event.key == pygame.K_c:
                        for cam in cams:
                            cam.use_conversion_methods = not cam.use_conversion_methods
                        print("cam.use_conversion_methods", cam.use_conversion_methods)
                    elif event.key == pygame.K_F2:
                        print(clock.get_fps())
                elif event.type == pygame.QUIT:
                    return
                    
            # move the cameras positions
            pressed = pygame.key.get_pressed()
            direction = Vec(0.0, 0.0)
            if pressed[pygame.K_RIGHT]:
                direction += Vec(1, 0)
            if pressed[pygame.K_LEFT]:
                direction -= Vec(1, 0)
            if pressed[pygame.K_UP]:
                direction += Vec(0, -1)
            if pressed[pygame.K_DOWN]:
                direction += Vec(0, 1)
            direction.length = 5
            cam2.position += direction
            
            # move other cam
            direction = Vec(0.0, 0.0)
            if pressed[pygame.K_d]:
                direction += Vec(1, 0)
            if pressed[pygame.K_a]:
                direction -= Vec(1, 0)
            if pressed[pygame.K_w]:
                direction += Vec(0, -1)
            if pressed[pygame.K_s]:
                direction += Vec(0, 1)
            direction.length = 5
            cam1.position += direction


    def demo_cam_tracking(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())

        num = 20
        balls = []
        for i in range(num):
            pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
            vel = Vec(random.randint(-5, 5), random.randint(-5, 5)) * 0.01
            balls.append(BallEnt(pos, vel))
        renderer.add_sprites(balls)
        col = random_rgb()
        for i in range(int(num)):
            pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
            spr = Sprite(create_random_surf(col), pos)
            renderer.add_sprite(spr)


        renderer.add_sprite(TextSprite("use mouse to select a rect to track", Point(0, 0), color=WHITE, \
                                                    z_layer=1000, anchor='center', parallax_factors=Vec(0.0, 0.0)))

        cam = Camera(screen.get_rect(), position=Point(400, 300))

        running = True
        clock = pygame.time.Clock()
        while running:
            dt = clock.tick(50)
            renderer.draw(screen, cam, fill_color=BLACK, do_flip=True)

            for ball in balls:
                ball.update(dt)

            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        return
                    elif event.key == pygame.K_c:
                        cam.use_conversion_methods = not cam.use_conversion_methods
                        print("cam.use_conversion_methods", cam.use_conversion_methods)
                    elif event.key == pygame.K_F2:
                        print(clock.get_fps())
                elif event.type == pygame.QUIT:
                    return
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1:
                        selection_rect = pygame.Rect(pygame.mouse.get_pos(), (1, 1))
                        marked = renderer.get_sprites_in_rect(selection_rect)
                        if marked:
                            cam.track = marked[0]
                        else:
                            mx, my = event.pos
                            cam.position = cam.screen_to_world(Vec(mx, my))


    def demo_mouse_select(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())

        num = 200
        balls = []
        for i in range(num):
            pos = Point(random.randint(0, SCREENSIZE[0]), random.randint(0, SCREENSIZE[1]))
            vel = Vec(random.randint(-5, 5), random.randint(-5, 5)) * 0.01
            ball = BallEnt(pos, vel)
            balls.append(ball)
            renderer.add_sprite(ball)
        renderer.add_sprite(TextSprite("use mouse buttons to resize selection rect", Point(50, SCREENSIZE[1] - 50), \
                                                                        color=WHITE, z_layer=10000, anchor='topleft'))

        selection_rect = pygame.Rect(pygame.mouse.get_pos(), (50, 50))
        cam = Camera(screen.get_rect(), position=Point(400, 300))
        running = True
        clock = pygame.time.Clock()
        while running:
            dt = clock.tick(50)
            renderer.draw(screen, cam, fill_color=BLACK, do_flip=False)
            marked = renderer.get_sprites_in_rect(selection_rect, False)
            for idx, mark in enumerate(marked):
                pygame.draw.rect(screen, (max((idx + 1) * 255/len(marked), 0), 0, 0, 100), mark.rect)
            pygame.draw.rect(screen, (255, 255, 0, 100), selection_rect, 1)
            pygame.display.flip()

            for ball in balls:
                ball.update(dt)

            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        return
                    elif event.key == pygame.K_c:
                        cam.use_conversion_methods = not cam.use_conversion_methods
                        print("cam.use_conversion_methods", cam.use_conversion_methods)
                    elif event.key == pygame.K_F2:
                        print(clock.get_fps())
                elif event.type == pygame.QUIT:
                    return
                elif event.type == pygame.MOUSEMOTION:
                    selection_rect.center = pygame.mouse.get_pos()
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1 or event.button == 4:
                        selection_rect.inflate_ip(20, 20)
                    else:
                        if selection_rect.size[0] > 20 and selection_rect.size[1] > 20: 
                            selection_rect.inflate_ip(-20, -20)

    class Markable(Sprite):
        marked = False
        color = None
        # stored_alpha = 255
        def on_click(self):
            if self.marked:
                self.marked = False
                self._image.fill(self.color)
                # self.alpha = self.stored_alpha
            else:
                self.marked = True
                self.color = self._image.get_at((0,0))
                layer = self.z_layer if self.z_layer <= 1.0 else 1
                layer = layer if layer > 0 else 0.1
                self._image.fill((50 + 200 * layer, 50 + 200 * layer, 0))
            self._update()
                # self.stored_alpha = self.alpha
                # self.alpha = 255 - int(200 * layer)
                        
    def demo_parallax_select(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())

        num = 200.0
        for i in range(int(num)):
            col = random.choice(list(pygame.color.THECOLORS.values()))
            
            surf = create_random_surf((col[0], col[1], col[2], 255))
            factors = Vec(i/num, i/num)
            z_layer = i/num
            if z_layer < 0.02:
                z_layer = 1000
                factors = Vec(0.0, 0.0)
            pos = Point(random.randint(-390, 390), random.randint(-290, 290))
            spr = Markable(surf, pos, z_layer=z_layer, parallax_factors=factors, anchor='center')
            # spr.alpha = random.randint(30, 255)
            # spr.image.set_alpha(128)
            renderer.add_sprite(spr)
            
        selection_rect = pygame.Rect(0, 0, 1, 1)
        cam = Camera(screen.get_rect())
        cam.use_conversion_methods = False
        running = True
        clock = pygame.time.Clock()
        while running:
            clock.tick()
            renderer.draw(screen, cam, fill_color=BLACK, do_flip=True)
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        return
                    elif event.key == pygame.K_c:
                        cam.use_conversion_methods = not cam.use_conversion_methods
                        print("cam.use_conversion_methods", cam.use_conversion_methods)
                    elif event.key == pygame.K_F2:
                        print(clock.get_fps())
                elif event.type == pygame.QUIT:
                    return
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    selection_rect.center = pygame.mouse.get_pos()
                    marked = renderer.get_sprites_in_rect(selection_rect)
                    if marked:
                        marked[0].on_click()

            pressed = pygame.key.get_pressed()
            direction = Vec(0.0, 0.0)
            if pressed[pygame.K_RIGHT]:
                direction += Vec(1, 0)
            if pressed[pygame.K_LEFT]:
                direction -= Vec(1, 0)
            if pressed[pygame.K_UP]:
                direction += Vec(0, -1)
            if pressed[pygame.K_DOWN]:
                direction += Vec(0, 1)
            direction.length = 5
            cam.position += direction


    def demo_parallax(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())

        num = 200.0
        for i in range(int(num)):
            col = random.choice(list(pygame.color.THECOLORS.values()))
            surf = create_random_surf(col)
            factors = Vec(i/num, i/num)
            z_layer = i/num
            if z_layer < 0.02:
                z_layer = 1000
                factors = Vec(0.0, 0.0)
            pos = Point(random.randint(-390, 390), random.randint(-290, 290))
            spr = Sprite(surf, pos, z_layer=z_layer, parallax_factors=factors, anchor='center')
            renderer.add_sprite(spr)
            # spr.alpha = random.randint(50, 200)
            tspr = TextSprite("prallax factors={0} z_layer={1}".format(factors.x, z_layer), pos, color=invert_color(col), \
                                    backcolor=col, z_layer=z_layer+0.00001, parallax_factors=factors, anchor='center')
            renderer.add_sprite(tspr)
            # tspr.alpha = random.randint(50, 200)
        spr = Sprite(surf, Point(), z_layer=100, parallax_factors=factors, anchor='center')
        renderer.add_sprite(spr)

        # fixed sprites


        cam = Camera(screen.get_rect())
        cam.use_conversion_methods = False
        running = True
        clock = pygame.time.Clock()
        while running:
            clock.tick()
            renderer.draw(screen, cam, fill_color=BLACK, do_flip=True)
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        return
                    elif event.key == pygame.K_c:
                        cam.use_conversion_methods = not cam.use_conversion_methods
                        print("cam.use_conversion_methods", cam.use_conversion_methods)
                    elif event.key == pygame.K_F2:
                        print(clock.get_fps())
                elif event.type == pygame.QUIT:
                    return

            pressed = pygame.key.get_pressed()
            direction = Vec(0.0, 0.0)
            if pressed[pygame.K_RIGHT]:
                direction += Vec(1, 0)
            if pressed[pygame.K_LEFT]:
                direction -= Vec(1, 0)
            if pressed[pygame.K_UP]:
                direction += Vec(0, -1)
            if pressed[pygame.K_DOWN]:
                direction += Vec(0, 1)
            if pressed[pygame.K_PAGEUP]:
                spr.parallax_factors += Vec(0.01, 0.01)
                print(spr.parallax_factors, spr.position)
            if pressed[pygame.K_PAGEDOWN]:
                spr.parallax_factors -= Vec(0.01, 0.01)
                print(spr.parallax_factors, spr.position)
            direction.length = 5
            cam.position += direction

    def demo_vectors(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())

        pos = Point(200, -100)
        spiral_pos = Point(-200, 0)
        for deg in range(0, 360, 30):
            color = pygame.Color(0, 0, 0, 255)
            color.hsva = (deg, 50, 100, 100)
            v = Vec(1.0, 0).rotated(deg)
            v.length = 0.5 * deg
            renderer.add_sprite(VectorSprite(v, spiral_pos, color=color))

            direction = Vec(100, 0).rotated(deg)
            label = TextSprite(str(round(direction.angle, 3)), Point() + 1.2 * direction, \
                                                    color=color, anchor="center")
                                                    # color=invert_color(color), anchor="center")
            renderer.add_sprite(label)
            renderer.add_sprite(VectorSprite(direction, Point(pos.x, pos.y), color, label=label))

        for i in range(10):
            col = random.choice(list(pygame.color.THECOLORS.values()))
            label = TextSprite("Point " + str(i), Point(10, 10), color=col, anchor="topleft")
            vspr = VectorSprite(Point(random.randint(50, 400), random.randint(50, 300)), Point(0.0, 0.0), color=col, label=label)
            renderer.add_sprite(label)
            renderer.add_sprite(vspr)


        cam = Camera(screen.get_rect())
        cam.use_conversion_methods = False
        running = True
        while running:
            renderer.draw(screen, cam, fill_color=BLACK, do_flip=True)
            event = pygame.event.wait()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    return
                elif event.key == pygame.K_c:
                    cam.use_conversion_methods = not cam.use_conversion_methods
                    print("cam.use_conversion_methods", cam.use_conversion_methods)
            elif event.type == pygame.QUIT:
                return

    def demo_transforms(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())
        # anchors = ["topleft", "bottomleft", "topright", "bottomright",
                    # "midtop", "midleft", "midbottom", "midright",
                    # "center", Vec(15, 5)]
        t = ['flipx', 'flipy', 'rot', 'zoom']
        import itertools
        combinations = []
        for i in range(len(t)+1):
            combinations += [sorted(list(c)) for c in itertools.permutations(t, i)]
        combinations = list(set([tuple(c) for c in combinations]))
        combinations.sort(key=lambda x: len(x))
        
        # for testing a the flip bug
        # combinations = (tuple(), 'flipy', 'flipx', ('flipx', 'flipy'), ('flipy', 'flipx'))
        
        count = math.ceil(math.sqrt(len(combinations)))

        w, h = screen.get_size()
        dx = w / (count + 1)
        dy = h / (count + 1)
        pos = Point(w // 2,  -h // 2)
        img = pygame.Surface((40, 40), pygame.SRCALPHA)
        img.fill((200, 200, 200))
        img.fill((255, 255, 255, 0), img.get_rect().inflate(-20, -20))
        
        pygame.draw.rect(img, (255, 255, 0), pygame.Rect(25, 25, 10, 10))
        pygame.draw.circle(img, (50, 200, 50), (30, 10), 7)
        pygame.draw.polygon(img, (255, 255, 255), [(5, 25), (5, 35), (15, 35)])
        pygame.draw.line(img, (255, 0, 0), (20, 20), (35, 20))
        pygame.draw.line(img, (0, 255, 0), (20, 20), (20, 35))

        red = pygame.Surface((2, 2))
        red.fill((255, 255, 255))
        offset = Vec(10.0, -10.0) # Vec(random.randint(-30, 30), random.randint(-30, 30))
        rnd_rot = 20 #random.randint(0, 360)
        rnd_zoom = round(random.random() * 0.5 + 0.4, 2)
        sprites = []
        rot_sprites = []
        zoom_sprites = []
        for idx, comb in enumerate(combinations):
            if idx % count == 0:
                pos = pos + Vec(-w+dx, dy)
            spr = Sprite(img, pos)
            spr.alpha = 125
            sprites.append(spr)
            # spr.anchor = offset.copy()
            spr.anchor = offset
            if 'flipx' in comb:
                spr.flipped_x = True
            if 'flipy' in comb:
                spr.flipped_y = True
            if 'rot' in comb:
                spr.rotation = rnd_rot
                rot_sprites.append(spr)
            if 'zoom' in comb:
                spr.zoom = rnd_zoom
                zoom_sprites.append(spr)
            cur_alpha = spr.alpha

            renderer.add_sprite(Sprite(red, pos, 'center', z_layer=1))
            renderer.add_sprite(TextSprite(str(comb), pos + Vec(0, 60 * (1 if (idx % 2 == 0) else 1)), \
                                font=pygame.font.Font(None, 15)))
            pos = pos + Vec(dx, 0)
            
        renderer.add_sprites(sprites)
        text = "offset: "+str(offset)+"    rot: "+str(rnd_rot)+"    zoom: "+str(rnd_zoom)
        text = TextSprite(text, Point(-w//2 + 20, -h//2 + 20), \
                                                                font=pygame.font.Font(None, 15), anchor="topleft")
        renderer.add_sprite(text)
        pos = Point(w//2 - 150, -h//2)
        renderer.add_sprite(TextSprite("press 'r' to rotate", pos + Vec(0, 20), \
                                                                font=pygame.font.Font(None, 15), anchor="topleft"))
        renderer.add_sprite(TextSprite("press 's' to zoom", pos + Vec(0, 40), \
                                                                font=pygame.font.Font(None, 15), anchor="topleft"))
        renderer.add_sprite(TextSprite("press 'a' to change alpha", pos + Vec(0, 60), \
                                                                font=pygame.font.Font(None, 15), anchor="topleft"))
        renderer.add_sprite(TextSprite("arrows to move offset", pos + Vec(0, 80), \
                                                                font=pygame.font.Font(None, 15), anchor="topleft"))
        cam = Camera(screen.get_rect())
        cam.use_conversion_methods = True
        running = True
        while running:
            renderer.draw(screen, cam, fill_color=(255, 0, 255), do_flip=True)
            event = pygame.event.wait()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    return
                elif event.key == pygame.K_a:
                    for spr in sprites:
                        spr.alpha = spr.alpha + 10 if spr.alpha < 255 else 15
                        cur_alpha = spr.alpha
                elif event.key == pygame.K_s:
                    for spr in zoom_sprites:
                        spr.zoom = spr.zoom + 0.05 if spr.zoom < 1.55 else 0.05
                        rnd_zoom = spr.zoom
                elif event.key == pygame.K_r:
                    rnd_rot = random.randint(0, 360)
                    for spr in rot_sprites:
                        # spr.rotation = rnd_rot
                        spr.rotation = spr.rotation + 10
                        rnd_rot = spr.rotation
                elif event.key == pygame.K_LEFT:
                    offset.x -= 1
                    for spr in sprites:
                        spr.anchor = Vec(offset.x, offset.y, 0)
                elif event.key == pygame.K_RIGHT:
                    offset.x += 1
                    for spr in sprites:
                        spr.anchor = offset
                elif event.key == pygame.K_UP:
                    offset.y += 1
                    for spr in sprites:
                        spr.anchor = offset
                elif event.key == pygame.K_DOWN:
                    offset.y -= 1
                    for spr in sprites:
                        spr.anchor = offset
                elif event.key == pygame.K_c:
                    cam.use_conversion_methods = not cam.use_conversion_methods
                    print("cam.use_conversion_methods", cam.use_conversion_methods)
                text.text = "offset: "+str(offset)+"    rot: "+str(rnd_rot)+"    zoom: "+str(rnd_zoom)+"    alpha:"+str(cur_alpha)
            elif event.type == pygame.QUIT:
                return


    def demo_anchors(screen, renderer):
        renderer.add_sprites(create_arrows_sprites())
        anchors = ["topleft", "bottomleft", "topright", "bottomright",
                    "midtop", "midleft", "midbottom", "midright",
                    "center", Vec(15, 5)]
        w, h = screen.get_size()
        dx = w / (len(anchors) + 1)
        pos = Point(dx - w // 2, 0)
        img = pygame.Surface((40, 40))
        img.fill((200, 200, 200))
        red = pygame.Surface((2, 2))
        red.fill((255, 0, 0))
        for idx, anch in enumerate(anchors):
            renderer.add_sprite(Sprite(img, pos, anch))
            renderer.add_sprite(Sprite(red, pos, 'center', z_layer=1))
            renderer.add_sprite(TextSprite(str(anch), pos + Vec(0, 60 * (-1 if (idx % 2 == 0) else 1)), font=pygame.font.Font(None, 15)))
            pos = pos + Vec(dx, 0)
        cam = Camera(screen.get_rect())
        cam.use_conversion_methods = True
        running = True
        while running:
            renderer.draw(screen, cam, fill_color=BLACK, do_flip=True)
            event = pygame.event.wait()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    return
                elif event.key == pygame.K_c:
                    cam.use_conversion_methods = not cam.use_conversion_methods
                    print("cam.use_conversion_methods", cam.use_conversion_methods)
            elif event.type == pygame.QUIT:
                return

    def demo_depth(screen, renderer):
        # create_arrows_sprites()
        renderer.add_sprite(TextSprite("press space", Point(400, 550), anchor="bottomleft"))
        dx = 10
        count = 30
        for i in range(count):
            img = pygame.Surface((30, 30))
            img.fill((random_rgb()))
            pos = Point(dx * i, dx * i)
            spr = Sprite(img, pos, 'topleft', z_layer=(i+1))
            renderer.add_sprite(spr)
            renderer.add_sprite(TextSprite(str(spr.z_layer), pos, font=pygame.font.Font(None, 15), \
                                                                        z_layer=spr.z_layer + 0.1, anchor="topleft"))
        img = pygame.Surface(screen.get_size())
        img.fill((255, 0, 0))
        img.set_alpha(128)
        sprite = Sprite(img, Point(), anchor="topleft", z_layer=0.5)
        text_sprite = TextSprite("z_layer = " + str(sprite.z_layer), \
                                                Point(20, 550), font=pygame.font.Font(None, 25), anchor="bottomleft")
        renderer.add_sprite(sprite)
        renderer.add_sprite(text_sprite)

        cam = Camera(screen.get_rect(), Point(400, 300))
        running = True
        while running:
            renderer.draw(screen, cam, fill_color=BLACK, do_flip=True)
            event = pygame.event.wait()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    return
                elif event.key == pygame.K_SPACE:
                    # sprite.z_layer = (sprite.z_layer + 1) % (count + 1)
                    renderer.update_z_layer(sprite, (sprite.z_layer + 1) % (count + 1))
                    text_sprite.text = "z_layer = " + str(sprite.z_layer)
            elif event.type == pygame.QUIT:
                return

    def error_msg(screen, renderer, error):
        # create_arrows_sprites()
        renderer.add_sprite(TextSprite("Oooops... how embarrassing, an error occured:", Point(400, 20), anchor="midtop"))
        renderer.add_sprite(TextSprite("press any key...", Point(400, 550), anchor="bottomleft"))
        renderer.add_sprite(TextSprite(str(error), Point(20, 50), font=pygame.font.Font(None, 15), anchor="topleft"))
        info = traceback.format_exc()
        pyfont = pygame.font.Font(None, 15)
        for idx, inf in enumerate(info.split('\n')):
            renderer.add_sprite(TextSprite(inf, Point(20, 100 + pyfont.get_height() * idx), font=pyfont, antialias=10, anchor="topleft"))

        cam = Camera(screen.get_rect(), Point(400, 300))
        running = True
        while running:
            renderer.draw(screen, cam, fill_color=BLACK, do_flip=True)
            event = pygame.event.wait()
            if event.type == pygame.KEYDOWN:
                return
            elif event.type == pygame.QUIT:
                return

                
    class Item(TextSprite):
        font = None
        fixed_point = "topleft"
        def __init__(self, key, text, action, pos):
            TextSprite.__init__(self, text, pos, anchor="topleft")
            self.key = key
            self.action = action

    def main():
        pygame.init()
        # screen = pygame.display.set_mode(SCREENSIZE, pygame.RESIZABLE | pygame.SRCALPHA, 32)
        screen = pygame.display.set_mode(SCREENSIZE)
        Item.font = pygame.font.Font(None, 30)
        items = [
                Item(pygame.K_ESCAPE, "esc to exit", None, Point()),
                Item(pygame.K_ESCAPE, "", None, Point()),
                Item(pygame.K_a, "a. anchors", demo_anchors, Point()),
                Item(pygame.K_b, "b. true z-depth", demo_depth, Point()),
                Item(pygame.K_c, "c. vector drawing", demo_vectors, Point()),
                Item(pygame.K_d, "d. pallax scrolling", demo_parallax, Point()),
                Item(pygame.K_e, "e. mouse selection", demo_mouse_select, Point()),
                Item(pygame.K_f, "f. cam tracking", demo_cam_tracking, Point()),
                Item(pygame.K_g, "g. split screen", demo_split_screen, Point()),
                Item(pygame.K_h, "h. transformations", demo_transforms, Point()),
                Item(pygame.K_i, "i. transformations performance", demo_transformation_performance, Point()),
                Item(pygame.K_j, "j. sprite performance", demo_sprite_performance, Point()),
                Item(pygame.K_k, "k. parallax sprite selection", demo_parallax_select, Point()),
                Item(pygame.K_l, "l. viewport", demo_viewport, Point()),
                Item(pygame.K_m, "m. interpolation", demo_interpolation, Point()),
                
                ]
                
        renderer = DefaultRenderer()
        renderer.add_sprites(items)
        dh = Point(0, items[0].font.get_linesize())
        for idx, item in enumerate(items):
            item.position = idx * dh

        running = True
        module_name = "pyknicpygame.spritesystem"
        caption = "demos for " + module_name
        pygame.display.set_caption(caption)
        w, h = screen.get_size()
        cam = Camera(screen.get_rect(), position=Point(0.4 * w, 0.4 * h))
        selected_item = None
        # renderer.add_sprites(create_arrows_sprites())

        while running:

            renderer.draw(screen, cam, fill_color=BLACK, do_flip=True)

            event = pygame.event.wait()

            if event.type == pygame.KEYDOWN:
                for item in items:
                    if event.key == item.key:
                        selected_item = item
            elif event.type == pygame.MOUSEBUTTONUP:
                hit_items = renderer.get_sprites_in_rect(pygame.Rect(event.pos, (1, 1)))
                if hit_items:
                    selected_item = hit_items[0]
            elif event.type == pygame.QUIT:
                running = False

            if selected_item:
                if selected_item.action:
                    renderer.push_sprites([])
                    pygame.display.set_caption(selected_item.text)
                    try:
                        selected_item.action(screen, renderer)
                    except Exception as e:
                        sys.stderr.write(traceback.format_exc())
                        error_msg(screen, renderer, e)
                    pygame.display.set_caption(caption)
                    renderer.pop_sprites()
                    selected_item = None
                else:
                    running = False

    def test_vector_class():
        v = Vec(1, 2)
        w = v * 3
        assert w.x == 3 and w.y == 6
        x = 4 * v
        assert x.x == 4 and x.y == 8

    test_vector_class()

    main()
    import sys
    sys.exit()

    # def test_pos():
        # class A(object):
            # def __init__(self):
                # self.pos = 0
                # self.old_pos = 0
            # def set_pos(self, p):
                # self.old_pos, self.pos = self.pos, p
        # class B(object):
            # def __init__(self):
                # self.pos = 0
                # self.old_pos = 0
            # def set_pos(self, p):
                # self.old_pos = self.pos
                # self.pos = p
        # class C(object):
            # def __init__(self):
                # self.pos = 0
                # self.old_pos = 0
            # @property
            # def position(self):
                # return self.pos
            # @position.setter
            # def position(self, p):
                # self.old_pos = self.pos
                # self.pos = p


        # a = A()
        # b = B()
        # c = C()
        # for i in range(1000000):
            # a.set_pos(i)
            # b.set_pos(i)
            # c.position = i

    # import cProfile
    # import datetime
    # t = datetime.datetime.now()

    # profile_name = "symple2.profile" # datetime.strftime("%Y-%m-%d-%H-%M-%S-") + "symple.profile"
    # cProfile.run('main()', profile_name)
    # # cProfile.run('test_pos()', profile_name)
    # import pstats
    # p = pstats.Stats(profile_name)
    # p.strip_dirs().sort_stats(-1).print_stats()
    # p.sort_stats('name')
    # p.print_stats()
    # p.sort_stats('cumulative').print_stats(20)


